Skip to content

fix(usd): review findings before the trade-time USD read side ships - #137

Merged
kevincodex1 merged 1 commit into
mainfrom
fix/trade-time-usd-review
Nov 10, 2026
Merged

kevincodex1 merged 1 commit into
mainfrom
fix/trade-time-usd-review

Conversation

@kevincodex1

Copy link
Copy Markdown
Member

Summary

Fixes from a strict review of the trade-time USD work (af4e6e5..133fabd: #133, #136), before the read side ships.

  • Community pairs are never stored (usdLedger.capturable). An outside-pool price can be pushed for a minute, and a captured value stayed in the totals forever. A suspended or delisted pair must show no USD, which a stored value defeated. Pair trades now always count at the pair's price now, which follows its listing status. The next backfill clears pair values captured since feat(usd): price every trade and fee at the time it happened #133.
  • A launch row with an unpriced part and no price right now shows its quote amount, not a smaller dollar figure that looks complete (launchVolumeUsd).
  • The receipt path no longer captures prices. The user is waiting there, and a capture could wait on Coinbase and Chainlink.
  • Heal pass (healUnpricedUsd), every 5 minutes per chain:
    • prices the last 3 days' trades and quote-side fees with no USD whose hour now has a price
    • adds them to the totals in the same statement
    • skips pairs, and re-tests usd IS NULL inside the UPDATE, so two machines change each row once
  • Backfill apply is one transaction under the rebuild-launch-totals.sql locks: pair cleanup, pricing and the recompute commit together. No window where values are priced but not summed, and a failure changes nothing. The dry run counts pairs as unpriced and stays write-free.
  • Per-launch lateral probe on a new partial index instead of aggregating every unpriced trade per request, plus a partial index for unpriced fees.
  • rebuild-launch-totals.sql recomputes the USD totals and reports USD drift.
  • Wording: the tooltip and the partial note. The backfill now retries network errors and timeouts.

Deferred (not risky): the pairs pages and the token fee panel still at today's price; volume-sort units during an ETH outage (pre-existing); per-hour source labels; small duplication cleanups.

Test plan

  • tsc, lint clean; npm test: 1410 tests, 1399 pass, 0 fail
  • Throwaway Postgres 16, with the real migration (3 partial indexes):
    • two concurrent heal passes priced the missed trade and fee once ($2,000 → $6,200, not $10,400; fee $21); reruns changed nothing; the pair stayed NULL
    • the dry run reported pairs as unpriced and changed nothing
    • apply (1 BEGIN / 1 COMMIT) cleared the old pair value and its ledger rows, and left the ETH launch at $6,200
    • the repair script: 0 USD drift
    • the real reads: ETH launch $6,200, pair row null (shows the quote amount), sort ok, totals $6,200 and partial
  • PR guard: 0 blocking

From a strict review of af4e6e5..133fabd (#133, #136):

- Community pairs are never stored (usdLedger.capturable). A pair priced from an
  outside pool can be pushed for a minute, and a captured price stayed in the
  totals for good. A suspended or delisted pair must show no USD, which a stored
  value defeated. Their trades keep usd NULL and count at the pair's price now,
  which follows its listing status. The backfill (apply) clears pair values
  captured since #133 and drops their ledger rows.
- A launch row whose volume is partly unpriced while its quote has no price
  right now shows its quote amount (launchVolumeUsd: null) instead of a smaller
  dollar figure that looked complete.
- The receipt path (applyLaunchTx, the user is waiting) no longer captures live
  prices: a capture could wait on Coinbase and Chainlink. Those trades take the
  hour's price the poller captured, or the heal pass prices them.
- Heal pass (usdLedger.healUnpricedUsd), every 5 minutes per chain after a sync:
  - prices the last 3 days' trades and quote-side fees with no USD whose hour now
    has a price, and adds them to the launch totals in the same statement
  - skips pairs and re-tests usd IS NULL inside the UPDATE, so two machines
    change each row and each total once
- Backfill apply is ONE transaction under the rebuild-launch-totals locks: pair
  cleanup, pricing and the totals recompute commit together. No reader sees rows
  priced but not yet summed, and a failure changes nothing. The dry run counts
  pairs as unpriced and stays write-free.
- The lists probe one launch at a time (LATERAL u on the new partial index
  bb_launch_swaps_usd_missing_token_idx) instead of aggregating every unpriced
  trade per request. The totals' unpriced-fee read gets
  bb_launch_fee_events_usd_missing_idx.
- scripts/rebuild-launch-totals.sql recomputes the USD totals too and reports
  USD drift before and after.
- Wording: the tooltip and the partial note describe what has no recorded value.
  The backfill retries network errors and timeouts, not only 429/5xx.

Deferred, not risky: the pairs pages and the token fee panel still use today's
price; the volume sort mixes units during an ETH price outage (pre-existing);
per-hour source labels; poolMaps / quoteUsdNow duplication.

Verified on a throwaway Postgres 16 with the real migration (3 partial indexes
built):
- two concurrent heal passes priced the missed trade and fee once (AETH $2,000 →
  $6,200, not $10,400; fee $21); a rerun and a throttled run changed nothing; the
  pair stayed NULL
- the dry run reported pairs as unpriced and changed nothing
- apply (1 BEGIN / 1 COMMIT) cleared the old pair value and its ledger rows and
  left AETH at $6,200
- the repair script ran with 0 USD drift
- the real reads: AETH $6,200, the pair row null (quote amount), sort ok, totals
  $6,200 and partial
tsc and lint clean; npm test 1410 (1399 pass, 11 skipped, 0 fail).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@kevincodex1
kevincodex1 merged commit 3479c06 into main Nov 10, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant